注意: 本章节会省略一些参数或方法的详细说明,因为在Form组件章节中提及到
ModelForm:
- 通过 Model类(即: models.py 中的表类) 生成 form类(即: Form组件中所要创建的类)
- Form 组件有的,ModelForm 组件也有,Form 组件没有的,ModelForm 组件也有
- 不要认为用了ModelForm组件就一定要用它的自动渲染form表单功能,ModelForm组件只用于验证,不做form表单渲染也是可以的,因为最后都是从 request.POST 中获取数据进行验证,前提是 form表单中的 name 需要和 form类中的字段名一致
ModelForm 的校验:
- ModelForm 的校验是根据 Model类(即: models.py 中的表类)中的 字段 或 字段参数来判断传递过来的数据是否正确(和Form组件中的校验情况差不多,只不过Form组件是用Form类中的字段进行校验,而ModelForm是用Model类中的字段进行校验)
- 在 Model类(即: models.py 中的表类)中,EmailField、 URLField、 UUIDField 等字段只有在使用ModelForm的时候才有意义,因为这些字段是在使用ModelForm的时候验证传递过来的数据是否正确的时候使用的
1. 本章节所用到的表
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=15)
price = models.IntegerField()
publish = models.ForeignKey(to='Publish')
author = models.ManyToManyField(to='Author')
def __str__(self):
return self.title
class Meta:
verbose_name = "书籍"
verbose_name_plural = verbose_name
class Publish(models.Model):
title = models.CharField(max_length=15)
def __str__(self):
return self.title
class Meta:
verbose_name = '出版社'
verbose_name_plural = verbose_name
class Author(models.Model):
name = models.CharField(max_length=15)
age = models.IntegerField()
authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE, verbose_name='作者详情') # 与AuthorDetail建立一对一的关系
def __str__(self):
return self.name
class Meta:
verbose_name = '作者'
verbose_name_plural = verbose_name
class AuthorDetail(models.Model):
birthday = models.DateField(verbose_name='生日')
telephone = models.BigIntegerField(verbose_name='电话')
addr = models.CharField(max_length=64, verbose_name='地址')
def __str__(self):
return str(self.telephone)
class Meta:
verbose_name = "作者详情表"
verbose_name_plural = verbose_name
2. 创建 ModelForm
- 通过Model类,生成form类
# views.py 或 forms.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from django.forms import ModelForm
from django.forms import widgets as wid # 因为widgets和ModelForm中的参数重名了,所以起个别名
class BookForm(ModelForm):
class Meta:
model = Book # 根据 Book 表类创建 Form 类
fields = '__all__' # 使用 Book 表类的所有字段,
# fields = ['title', 'price', 'publish', 'author'] # 使用 Book 表类的某几个字段
# exclude = ['publish', 'author'] # 排除某些字段,一般和 fields = '__all__' 搭配使用
labels = { # 和 Form 组件中的 label 作用是一样的
'title': '书籍名称',
'price': '价格',
'publish': '出版社',
'author': '作者'
}
error_messages = { # 错误信息
'title': {'required': "书籍名称不能为空"},
'price': {'required': "价格不能为空"},
'publish': {'required': "请选择出版社"},
'author': {'required': "请选择作者"},
}
widgets = { # 和 Form 组件中的 widgets作用是一样的
'title': wid.TextInput(attrs={'class': 'form-control'}),
'price': wid.TextInput(attrs={'class': 'form-control'}),
'publish': wid.Select(attrs={'class': 'form-control'}),
'author': wid.CheckboxSelectMultiple(attrs={'class': 'form-control'})
}
3. 使用 ModelForm 渲染 Form 表单
- 和 Form 组件的使用方式是一样的
# views.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from django.forms import ModelForm
from django.forms import widgets as wid
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
labels = {
'title': '书籍名称',
'price': '价格',
'publish': '出版社',
'author': '作者'
}
error_messages = {
'title': {'required': "书籍名称不能为空"},
'price': {'required': "价格不能为空"},
'publish': {'required': "请选择出版社"},
'author': {'required': "请选择作者"},
}
widgets = {
'title': wid.TextInput(attrs={'class': 'form-control'}),
'price': wid.TextInput(attrs={'class': 'form-control'}),
'publish': wid.Select(attrs={'class': 'form-control'}),
'author': wid.CheckboxSelectMultiple(attrs={'class': 'form-control'})
}
def add_book(request):
book_form = BookForm() # 实例化一个表单类
return render(request, 'add.html', {'book_form': book_form}) # 将表单类传递给HTML模板,然后渲染出form表单
# add.html
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in book_form %}
<div>
{{ field.label }}
{{ field }}
</div>
{% endfor %}
<input type="submit">
</form>
4.使用 ModelForm 渲染 Form 表单,并且将对应数据自动填写到表单中(即: 修改页面)
- 通过Form组件渲染form表单,无法将对应数据自动填写到表单中(即:无法实现修改页面的自动填充数据效果),但是 ModelForm 可以实现
- instance 参数接收一个对象(即: 查询数据后得到的对象)
# views.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from django.forms import ModelForm
from django.forms import widgets as wid
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
labels = {
'title': '书籍名称',
'price': '价格',
'publish': '出版社',
'author': '作者'
}
error_messages = {
'title': {'required': "书籍名称不能为空"},
'price': {'required': "价格不能为空"},
'publish': {'required': "请选择出版社"},
'author': {'required': "请选择作者"},
}
widgets = {
'title': wid.TextInput(attrs={'class': 'form-control'}),
'price': wid.TextInput(attrs={'class': 'form-control'}),
'publish': wid.Select(attrs={'class': 'form-control'}),
'author': wid.CheckboxSelectMultiple(attrs={'class': 'form-control'})
}
def edit_book(request, edit_book_id):
edit_book_obj = Book.objects.filter(pk=edit_book_id).first() # 查询指定的book数据
book_form = BookForm(instance=edit_book_obj) # 将查询到的数据传递给 instance 参数,从而实现将对应数据自动填写到表单中(即: 修改页面)
return render(request, 'add.html', {'book_form': book_form})
# edit.html
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in book_form %}
<div>
{{ field.label }}
{{ field }}
</div>
{% endfor %}
<input type="submit">
</form>
5. 自定义字段所输出的表单 HTML
# datetime_picker.html
<div class="input-group date form_date">
<input readonly class="form-control"
type="{{ widget.type }}"
name="{{ widget.name }}"
{% if widget.value != None %} value="{{ widget.value|stringformat:"s" }}" {% endif %}
{% include "django/forms/widgets/attrs.html" %} />
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
</div>
# widgets.py
from django import forms
class DateTimePickerInput(forms.TextInput):
template_name = 'forms/widgets/datetime_picker.html'
# forms/coupon.py
from django.forms import ModelForm
from jyld_backstage.models import *
from django.forms import widgets as wid
from xadmin.forms.widgets import DateTimePickerInput
class CouponModelForm(ModelForm):
class Meta:
model = Coupon
fields = '__all__'
widgets = {
'title': wid.TextInput(attrs={'class': 'form-control'}),
'open_date': DateTimePickerInput,
}

6. 获取 ModelForm 字段的相关信息
# views.py
from django.shortcuts import render
from app01.models import *
from django.forms import ModelForm
class UserInfoForm(ModelForm):
class Meta:
model = UserInfo
fields = '__all__'
labels = {
'username': '用户名',
'phone': '电话号码',
'age': '年龄',
}
def edit_data(request):
user = UserInfo.objects.all().first()
form = UserInfoForm(instance=user)
# 获取当前标签的相关信息
# form['username'] 等同于 html 模板中循环出来的 field
print(form['username'].__dict__) # {'label': '用户名', 'name': 'username', ……}
# 获取当前标签属性的内容(如: class, id, value 等)
for data in form['username']:
# data 等同于 html 模板中循环出来的 field
print(data.__dict__) # {'data': {'required': True, 'type': 'text', 'name': 'username', 'template_name': 'django/forms/widgets/text.html', 'is_hidden': False, 'value': 'Amy', 'attrs': {'maxlength': '32', 'id': 'id_username', 'required': True'+'}'+'}'}}, ……}
return render(request, 'change.html', {
'form': form
})
# xxx.html
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label }}:
<input
type="{{'{'+'{'+' field.0.data.type '+'}'+'}'}}"
value="{{'{'+'{'+' field.0.data.value '+'}'+'}'}}"
id="{{'{'+'{'+' field.0.data.attrs.id '+'}'+'}'}}"
maxlength="{{'{'+'{'+' field.0.data.attrs.maxlength '+'}'+'}'}}"
name="{{ field.name }}"
>
</p>
{% endfor %}
<input type="submit" value="提交">
</form>
7. 添加数据
- 直接通过 ModelForm 中的 .save() 方法将提交过来的数据添加到数据库中,如果是使用 form 组件就需要自己写 orm 语句将提交过来的数据添加数据库中
- .save() 方法的返回值就是所添加的该条数据对象
# views.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from django.forms import ModelForm
from django.forms import widgets as wid
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
labels = {
'title': '书籍名称',
'price': '价格',
'publish': '出版社',
'author': '作者'
}
error_messages = {
'title': {'required': "书籍名称不能为空"},
'price': {'required': "价格不能为空"},
'publish': {'required': "请选择出版社"},
'author': {'required': "请选择作者"},
}
widgets = {
'title': wid.TextInput(attrs={'class': 'form-control'}),
'price': wid.TextInput(attrs={'class': 'form-control'}),
'publish': wid.Select(attrs={'class': 'form-control'}),
'author': wid.CheckboxSelectMultiple(attrs={'class': 'form-control'})
}
def add_book(request):
if request.method == 'POST':
book_form = BookForm(request.POST)
if book_form.is_valid():
book_data = book_form.save() # 将提交过来的数据保存到数据库
print(book_data.pk, book_data.title) # 10 三国演义
return redirect('/books/')
else:
print(book_form.errors)
return HttpResponse('添加失败')
book_form = BookForm()
return render(request, 'add.html', {'book_form': book_form})
# add.html
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in book_form %}
<div>
{{ field.label }}
{{ field }}
</div>
{% endfor %}
<input type="submit">
</form>
8. 修改数据
- 直接通过 ModelForm 中的 instance 参数修改数据库中的数据,如果是使用 form 组件就需要自己写 orm 语句修改数据库中的数据
- instance 参数接收一个对象(即: 查询数据后得到的对象)
# views.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from django.forms import ModelForm
from django.forms import widgets as wid
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
labels = {
'title': '书籍名称',
'price': '价格',
'publish': '出版社',
'author': '作者'
}
error_messages = {
'title': {'required': "书籍名称不能为空"},
'price': {'required': "价格不能为空"},
'publish': {'required': "请选择出版社"},
'author': {'required': "请选择作者"},
}
widgets = {
'title': wid.TextInput(attrs={'class': 'form-control'}),
'price': wid.TextInput(attrs={'class': 'form-control'}),
'publish': wid.Select(attrs={'class': 'form-control'}),
'author': wid.CheckboxSelectMultiple(attrs={'class': 'form-control'})
}
def edit_book(request, edit_book_id):
edit_book_obj = Book.objects.filter(pk=edit_book_id).first()
if request.method == 'POST':
book_form = BookForm(request.POST, instance=edit_book_obj) # 验证提交过来的数据是否有误,并且通过 instance 参数指定要修改那条数据
# 如果没有指定 instance 参数那么在执行 .save() 方法的时候该操作就是新增数据的操作
# 不要和下面的 instance 参数搞混了,虽然 instance 参数的作用是一样的,但是表达的意思是不一样的
#(即:下面instance参数表达的是将对应数据自动填写到表单中,而这里表达的是指定要修改那条数据)
if book_form.is_valid():
book_data = book_form.save()
print(book_data.pk, book_data.title) # 10 三国演义2
return redirect('/books/')
else:
return HttpResponse('修改失败')
book_form = BookForm(instance=edit_book_obj) # 将查询到的数据传递给 instance 参数,从而实现将对应数据自动填写到表单中(即: 修改页面)
return render(request, 'add.html', {'book_form': book_form})
# edit.html
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in book_form %}
<div>
{{ field.label }}
{{ field }}
</div>
{% endfor %}
<input type="submit">
</form>
9. 自定义字段
- 注意:
- 定义自定义字段的方法和 form 组件定义字段的方法是一样的
- 自定义字段的字段名不能和 ModelForm 中所指向的 表类(即: Model类)中的字段名重复
- 因为如果自定义字段的字段名和当前 ModelForm 所指向的 表类(即: Model类)中的字段名一样,那么该自定义字段就是 表类(即: Model类)的字段而不是自定义字段
- 使用方式一 -> 确认密码
# forms/password.py
from django.forms import ModelForm
from django.forms import widgets as wid
from django.forms import fields as Ffields
from django.core.exceptions import ValidationError
from .models import *
class RegForm(ModelForm):
confirm_password = Ffields.CharField(
min_length=6,
label='确认密码',
widget=wid.PasswordInput(
attrs={
'class': 'form-control',
'placeholder': '确认密码',
}
),
error_messages={
'required': '确认密码不能为空',
'min_length': '确认密码不能小于6位',
}
)
class Meta:
model = UserInFo
fields = ['password', 'confirm_password']
labels = {
'password': '密码',
}
error_messages = {
'password': {'required': '密码不能为空', 'min_length': '密码不能小于6位', },
}
widgets = {
'password': wid.PasswordInput(attrs={'class': 'form-control', 'placeholder': '密码'}),
}
# 重写全局钩子,判断两次密码是否一致
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if password == confirm_password:
return self.cleaned_data
else:
self.add_error('confirm_password', ValidationError('两次密码不一致'))
- 使用方式二 -> 添加当前表的一对一字段所关联表的内容到当前 form 表单中
- 自定义字段一般用于当前表类拥有一对一字段,且一对多或多对多很少会用到
- ModelForm 源码中两个重要属性:
- self.instance -> 字段对象
- self.initial -> 初始值(即: 修改页面所显示的每个字段所对应的数据)
# forms/author.py
from django import forms
from django.forms import ModelForm
from app01.models import *
class BootStrapModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # 调用父类的 __init__ 方法
# 批量添加样式
for field in iter(self.fields):
self.fields[field].widget.attrs.update({
'class': 'form-control'
})
class AuthorForm(BootStrapModelForm):
# 自定义字段
birthday = forms.DateField(label='出生日期')
telephone = forms.IntegerField(label='手机号码')
addr = forms.CharField(label='地址')
class Meta:
model = Author
fields = ['name', 'age']
labels = {
'name': '作者名称',
'age': '年龄',
}
error_messages = {
'name': {'required': "作者名称不能为空"},
'age': {'required': "年龄不能为空"},
'birthday': {'required': "出生日期不能为空"},
'telephone': {'required': "手机号码不能为空"},
'addr': {'required': "地址不能为空"},
}
# 重写 ModelForm 的 __init__ 方法
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # 调用父类的 __init__ 方法
# self.instance -> 字段对象
# self.initial -> 初始值(即: 修改页面所显示的每个字段所对应的数据)
# 修改数据时所显示的自定义字段的对应数据
if self.instance.authorDetail_id: # 使用字段对象下的 一对一 或 多对多 字段属性进行判断
self.initial['birthday'] = self.instance.authorDetail.birthday
self.initial['telephone'] = self.instance.authorDetail.telephone
self.initial['addr'] = self.instance.authorDetail.addr
# 重写 ModelForm 的 save 方法
def save(self, commit=True):
# self.instance -> 字段对象
# self.initial -> 初始值(即: 修改页面所显示的每个字段所对应的数据)
# 注意: 必须将修改数据的判断放到添加数据的判断前面
# 修改数据时自定义字段数据的保存
if self.instance.authorDetail_id: # 使用字段对象下的 一对一 或 多对多 字段属性进行判断
authordetail_queryset = AuthorDetail.objects.filter(pk=self.instance.authorDetail_id).all()
authordetail_queryset.update(
birthday=self.cleaned_data['birthday'],
telephone=self.cleaned_data['telephone'],
addr=self.cleaned_data['addr'],
)
# 该判断中所执行的内容,只适用于当前 Model 表中有 一对一、一对多、多对多字段
# 新建数据时自定义字段数据的保存
if not self.instance.authorDetail_id: # 使用字段对象下的 一对一 或 多对多 字段属性进行判断
# 新建数据时先新建 外键字段 或 一对一字段 的数据
author_detail_obj = AuthorDetail.objects.create(
birthday=self.cleaned_data['birthday'],
telephone=self.cleaned_data['telephone'],
addr=self.cleaned_data['addr']
)
# 将新建好的数据 id 赋值给当前字段对象的指定 外键字段 或 一对一字段 上
self.instance.authorDetail_id = author_detail_obj.pk
super().save(commit=True) # 调用父类 ModelForm 的 save 保存方法
return self.instance # 一定要返回 self.instance
# views.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from app01.forms.author import AuthorForm
def add_author(request):
author_form = AuthorForm()
if request.method == 'POST':
author_form = AuthorForm(request.POST)
if author_form.is_valid():
author_form.save()
return redirect('/authors/')
return render(request, 'change.html', {'form': author_form})
def edit_author(request, edit_author_id):
edit_author_obj = Author.objects.filter(pk=edit_author_id).first()
author_form = AuthorForm(instance=edit_author_obj)
if request.method == 'POST':
author_form = AuthorForm(request.POST, instance=edit_author_obj)
if author_form.is_valid():
author_form.save()
return redirect('/authors/')
return render(request, 'change.html', {'form': author_form})
# change.html
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group {% if field.errors %} has-error {% endif %}">
{{ field.label }}
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit" class="btn btn-success">
</form>


10. 保存或修改上传文件
# forms/picture.py
from django.forms import ModelForm
from app01.models import *
from django.forms import widgets as wid
class PictureForm(ModelForm):
class Meta:
model = Picture
fields = '__all__'
labels = {
'title': '标题',
'img': '图片',
}
error_messages = {
'title': {'required': "标题不能为空"},
'img': {'required': "图片不能为空"},
}
widgets = {
'title': wid.TextInput(attrs={'class': 'form-control'}),
'img': wid.FileInput(attrs={'class': 'form-control'}),
}
# views.py
from app01.models import *
from django.shortcuts import render, HttpResponse
from app01.forms.picture import PictureForm
def add_data(request):
picture_form = PictureForm()
if request.POST:
picture_form = PictureForm(request.POST, request.FILES)
if picture_form.is_valid():
picture_form.save()
return HttpResponse('添加成功')
return render(request, 'change.html', {
'form': picture_form,
})
def edit_data(request, edit_pk):
picture_obj = Picture.objects.filter(pk=edit_pk).first()
picture_form = PictureForm(instance=picture_obj)
if request.POST:
picture_form = PictureForm(request.POST, request.FILES, instance=picture_obj)
if picture_form.is_valid():
picture_form.save()
return HttpResponse('修改成功')
return render(request, 'change.html', {
'form': picture_form,
})
# change.html
<form method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
{{ field.label }}
{{ field }}
</div>
<p>{{ field.errors.0 }}</p>
{% endfor %}
<input type="submit" value="提交">
</form>
11. .queryset.model -> 获取该字段对象下所关联的模型表(即: 表类)
- 获取form的字段对象方法在Form组件章节中有提到
- .queryset 只作用于 一对一、一对多、多对多字段
- 语法: 字段对象.queryset.model
# views.py 或 forms.py
from django.forms import ModelForm
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
book_form_obj = BookForm()
# 获取 一对多 的字段对象
field_obj = book_form_obj.fields['publish'] # <django.forms.models.ModelChoiceField object at 0x000001F2AC79E7B8>
# 获取该字段对象下所关联的模型表
model = field_obj.queryset.model # <class 'app01.models.Publish'>
# 获取该模型表下的数据
model_data = model.objects.all() # <QuerySet [<Publish: 东莞出版社>, <Publish: 广州出版社>]>
# 获取该模型表所在的app名称
app_label = model._meta.app_label # app01
# 获取该模型表的表名
model_name = model._meta.model_name # publish
12. 只用 ModelFo组件 进行验证,不做渲染
- 不要认为用了ModelForm组件就一定要用它的自动渲染form表单功能,ModelForm组件只用于验证,不做form表单渲染也是可以的,因为最后都是从 request.POST 中获取数据进行验证,前提是 form表单中的 name 需要和 form类中的字段名一致
- 只用ModelForm组件进行验证,不做渲染(即: 自己编写form表单) -> 使用 form 表单提交数据
# 这里就不写例子了,因为和form组件章节中的列子差不多
13. 图书管理例子
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=15)
price = models.IntegerField()
publish = models.ForeignKey(to='Publish')
author = models.ManyToManyField(to='Author')
def __str__(self):
return self.title
class Meta:
verbose_name = "书籍"
verbose_name_plural = verbose_name
class Publish(models.Model):
title = models.CharField(max_length=15)
def __str__(self):
return self.title
class Meta:
verbose_name = '出版社'
verbose_name_plural = verbose_name
class Author(models.Model):
name = models.CharField(max_length=15)
def __str__(self):
return self.name
class Meta:
verbose_name = '作者'
verbose_name_plural = verbose_name
# views.py
from django.shortcuts import render, HttpResponse, redirect
from .models import *
from django.forms import ModelForm
from django.forms import widgets as wid # 因为widgets和ModelForm中的参数重名了,所以起个别名
class BookForm(ModelForm):
class Meta:
model = Book # 根据 Book 表类创建 Form 类
fields = '__all__' # 使用 Book 表类的所有字段,
# fields = ['title', 'price', 'publish', 'author'] # 使用 Book 表类的某几个字段
# exclude = ['publish', 'author'] # 排除某些字段,一般和 fields = '__all__' 搭配使用
labels = { # 和 Form 组件中的 label 作用是一样的
'title': '书籍名称',
'price': '价格',
'publish': '出版社',
'author': '作者'
}
error_messages = { # 错误信息
'title': {'required': "书籍名称不能为空"},
'price': {'required': "价格不能为空"},
'publish': {'required': "请选择出版社"},
'author': {'required': "请选择作者"},
}
widgets = { # 和 Form 组件中的 widgets作用是一样的
'title': wid.TextInput(attrs={'class': 'form-control'}),
'price': wid.TextInput(attrs={'class': 'form-control'}),
'publish': wid.Select(attrs={'class': 'form-control'}),
'author': wid.SelectMultiple(attrs={'class': 'form-control'})
}
def books(request):
book_list = Book.objects.all()
return render(request, 'books.html', {'book_list': book_list})
def add_book(request):
book_form = BookForm()
if request.method == 'POST':
book_form = BookForm(request.POST)
if book_form.is_valid():
book_form.save() # 将提交过来的数据保存到数据库
return redirect('/books/')
return render(request, 'add.html', {'book_form': book_form})
def edit_book(request, edit_book_id):
edit_book_obj = Book.objects.filter(pk=edit_book_id).first()
book_form = BookForm(instance=edit_book_obj)
if request.method == 'POST':
book_form = BookForm(request.POST, instance=edit_book_obj) # 验证提交过来的数据是否有误,并且通过 instance 参数指定要修改那条数据
# 如果没有指定 instance 参数那么在执行 .save() 方法的时候该操作就是新增数据的操作
# 不要和上面的 instance 参数搞混了,虽然 instance 参数的作用是一样的,但是表达的意思是不一样的
#(即:上面instance参数表达的是将对应数据自动填写到表单中,而这里表达的是指定要修改那条数据)
if book_form.is_valid():
book_form.save()
return redirect('/books/')
return render(request, 'edit.html', {'book_form': book_form})
# urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^books', views.books),
url(r'^add_book', views.add_book),
url(r'^edit_book/(\d+)', views.edit_book),
# url(r'^delete_book', views.delete_book),
]
# books.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Title</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<a href="/add_book/" class="btn btn-info">添加书籍</a>
<table border="1" class="table table-bordered">
<thead>
<tr>
<th>id</th>
<th>书名</th>
<th>价格</th>
<th>出版社</th>
<th>作者</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for book in book_list %}
<tr>
<td>{{ book.pk }}</td>
<td>{{ book.title }}</td>
<td>{{ book.price }}</td>
<td>{{ book.publish.title }}</td>
<td>
{% for author in book.author.all %}
{{ author.name }}
{% endfor %}
</td>
<td>
<a href="/edit_book/{{ book.pk }}">编辑</a>
{% comment %}<a href="/delete_book/{{ book.pk }}">删除</a>{% endcomment %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
# form.html
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in book_form %}
<div class="form-group {% if field.errors %} has-error {% endif %}">
{{ field.label }}
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit" class="btn btn-success">
</form>
# add.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Title</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h3>添加页面</h3>
{% include 'form.html' %}
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
# edit.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Title</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h3>修改页面</h3>
{% include 'form.html' %}
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>